/**
 * Copyright (c) 2014 - 2020 Qualcomm Technologies International, Ltd.
 * \defgroup aec_reference_cap
 *
 * \file  aec_reference_cap_c.h
 *
 * AEC Reference Capability
 * \ingroup capabilities
 */
#ifndef AEC_REFERENCE_CAP_C_H
#define AEC_REFERENCE_CAP_C_H

/****************************************************************************
Include Files
*/

#include "capabilities.h"

#include "aec_reference_gen_c.h"

#include "aec_reference.h"
#include "aec_reference_latency.h"

#include "pl_timers/pl_timers.h"
#include "cbops/cbops_c.h"
#include "volume/shared_volume_control.h"
#ifdef INSTALL_DELEGATE_RATE_ADJUST_SUPPORT
#include "stream/stream_delegate_rate_adjust.h"
#endif

#if defined(CHIP_BASE_HYDRA)
/* speaker and mic shifts are done in HW */
#define AEC_REFERENCE_DEFAULT_EP_SHIFT 0
#else
#define AEC_REFERENCE_DEFAULT_EP_SHIFT 8
#endif

#if defined(CHIP_BASE_HYDRA)
/* new amount that speaker consumed in calculated by op at
 * the start of handler. This way there will be less jitter
 * in the calculated rate, and better reference synchronisation
 * when SW rate adjustment is used. This is doable only for
 * HYDRA platforms.
 */
#define AEC_REF_CALC_SPKR_RATE_MONITOR_AMOUNT

/* This will enable running an inplace sidetone graph at low
 * period while main task period is too high for sidetone latency
 *
 * Note: Not enabled yet.
 */
#define AEC_REFERENCE_CAN_PERFORM_INPLACE_SIDETONE_MIXING
#ifdef AEC_REFERENCE_CAN_PERFORM_INPLACE_SIDETONE_MIXING
#define AEC_REFERENCE_SYNC_SIDETONE_GRAPH_TO_SPKR
#endif

/*This will make sure mics have same amount of data when started first time*/
#define FORCE_RESYNC_ENABLE 
#endif /* CHIP_BASE_HYDRA */
/****************************************************************************
Public Type Declarations
*/
#define MAX_NUMBER_MICS     8
#define MAX_NUMBER_SPEAKERS 8

/* Capability Version */
#define AEC_REFERENCE_CAP_VERSION_MINOR            11

/* Task period default and limits */
#define AEC_REFERENCE_DEFAULT_TASK_PERIOD          1000   /* default task period in us */
#define AEC_REFERENCE_MAX_TASK_PERIOD              5000   /* maximum task period in us */
#define AEC_REFERENCE_MIN_TASK_PERIOD              1000   /* minimum task period in us */

/* Enable speaker TTP playback if enabled by build */
#if defined(INSTALL_AEC_REFERENCE_SPKR_TTP)
#define AEC_REFERENCE_SPKR_TTP
/* support generating timestamps for mic path */
#define AEC_REFERENCE_GENERATE_MIC_TIMESTAMP
#define AEC_REFERENCE_MIC_METADATA_MIN_TAG_LEN 60

#include "ttp/timed_playback.h"
#include "ttp/ttp.h"
#include "ttp/ttp_pid.h"
#include "ttp_utilities.h"
#endif

/* Enable low latency sidetone path in AEC_REF
 * This is enabled only if AEC_REFERENCE_CAN_PERFORM_INPLACE_SIDETONE_MIXING
 * is defined.
 */
#ifdef AEC_REFERENCE_CAN_PERFORM_INPLACE_SIDETONE_MIXING
#define AEC_REFERENCE_ENABLE_LOW_LATENCY_PATH
#define AEC_REFERENCE_ENABLE_STEREO_ST_MIC
#define AEC_REFERENCE_LL_FW_NUM_SCRATCH_BUFF 4
/* Enable this switch to run a standalone sidetone path
 * without the backend speaker graph connected.
 * Disable for now.
 */
//#define AEC_REFERENCE_ENABLE_STANDALONE_ST_PATH
#include "cbops_aec_ref_llm_custom_op.h"
#endif

#define AEC_REF_INPUT_TERMINAL1 0
#define AEC_REF_INPUT_TERMINAL2 1
#define AEC_REF_MIC_TERMINAL1   2
#define AEC_REF_MIC_TERMINAL2   3
#define AEC_REF_MIC_TERMINAL3   4
#define AEC_REF_MIC_TERMINAL4   5
#define AEC_REF_INPUT_TERMINAL3 6
#define AEC_REF_INPUT_TERMINAL4 7
#define AEC_REF_INPUT_TERMINAL5 8
#define AEC_REF_INPUT_TERMINAL6 9
#define AEC_REF_INPUT_TERMINAL7 10
#define AEC_REF_INPUT_TERMINAL8 11
#define AEC_REF_MIC_TERMINAL5   12
#define AEC_REF_MIC_TERMINAL6   13
#define AEC_REF_MIC_TERMINAL7   14
#define AEC_REF_MIC_TERMINAL8   15

#define AEC_REF_NUM_SINK_TERMINALS  16

#define AEC_REF_REFERENCE_TERMINAL  0
#define AEC_REF_SPKR_TERMINAL1      1
#define AEC_REF_SPKR_TERMINAL2      2
#define AEC_REF_OUTPUT_TERMINAL1    3
#define AEC_REF_OUTPUT_TERMINAL2    4
#define AEC_REF_OUTPUT_TERMINAL3    5
#define AEC_REF_OUTPUT_TERMINAL4    6
#define AEC_REF_SPKR_TERMINAL3      7
#define AEC_REF_SPKR_TERMINAL4      8
#define AEC_REF_SPKR_TERMINAL5      9
#define AEC_REF_SPKR_TERMINAL6      10
#define AEC_REF_SPKR_TERMINAL7      11
#define AEC_REF_SPKR_TERMINAL8      12
#define AEC_REF_OUTPUT_TERMINAL5    13
#define AEC_REF_OUTPUT_TERMINAL6    14
#define AEC_REF_OUTPUT_TERMINAL7    15
#define AEC_REF_OUTPUT_TERMINAL8    16
#ifdef ENABLE_STEREO_REFERENCE
#define AEC_REF_REFERENCE_TERMINAL1 17

#define AEC_REF_NUM_SOURCE_TERMINALS    18
#else
#define AEC_REF_NUM_SOURCE_TERMINALS    17
#endif


/* Resampler Inter-stage scratch buffer.

   Worse case when resampling from 48k to 44.1k the first
   stage does an upsample by a factor of 2.1.
      48000.0 (fs in : 21/10) --> 100800.0 (fs internal : 7/16) --> 44100.0 (fs out)

   Space for a period of 2 msec would be 201.6 samples (add a little cushion use 204)
*/
#define AEC_REF_RESAMPLER_TEMP_MIN_BUF_SIZE 204

#define AEC_NUM_SCRATCH_BUFFERS     16

#define CHAN_STATE_MIC_MASK   (AEC_REFERENCE_CONSTANT_CONN_MIKE_1 |\
                               AEC_REFERENCE_CONSTANT_CONN_MIKE_2 |\
                               AEC_REFERENCE_CONSTANT_CONN_MIKE_3 |\
                               AEC_REFERENCE_CONSTANT_CONN_MIKE_4 |\
                               AEC_REFERENCE_CONSTANT_CONN_MIKE_5 |\
                               AEC_REFERENCE_CONSTANT_CONN_MIKE_6 |\
                               AEC_REFERENCE_CONSTANT_CONN_MIKE_7 |\
                               AEC_REFERENCE_CONSTANT_CONN_MIKE_8 |\
                               AEC_REFERENCE_CONSTANT_CONN_MIKE_1_INPUT_ONLY)

#define CHAN_STATE_SPKR_MASK  (AEC_REFERENCE_CONSTANT_CONN_SPKR_1 |\
                               AEC_REFERENCE_CONSTANT_CONN_SPKR_2 |\
                               AEC_REFERENCE_CONSTANT_CONN_SPKR_3 |\
                               AEC_REFERENCE_CONSTANT_CONN_SPKR_4 |\
                               AEC_REFERENCE_CONSTANT_CONN_SPKR_5 |\
                               AEC_REFERENCE_CONSTANT_CONN_SPKR_6 |\
                               AEC_REFERENCE_CONSTANT_CONN_SPKR_7 |\
                               AEC_REFERENCE_CONSTANT_CONN_SPKR_8 |\
                               AEC_REFERENCE_CONSTANT_CONN_SPKR_1_OUTPUT_ONLY)


#define AEC_REFERENCE_REF_RATE_UPDATE_PERIOD 9 /* in number of timer period (1ms) */

/* expected maximum jitter in consuming sidetone by speaker path, in millisecond */
#define AEC_REF_SIDETONE_CONSUMING_JITTER_MS 0.5

/* maximum number of sidetone sources:
 * Note: Currently it is limited to one source, changing
 *       this will only affects memory reserving for future expanding.
 * */
#define AEC_REFERENCE_MAX_SIDETONE_MICS 2

#ifdef AEC_REFERENCE_ENABLE_LOW_LATENCY_PATH
/* In low latency mode, the AEC-REF's sidetone path will be kicked
 * at 500 us
 */
#define AEC_REFERENCE_SIDETONE_PATH_TASK_PERIOD 500
#endif

typedef enum
{
    /* Sidetone isn't required or
     * it's irrelevant under current config,
     * (e.g. there is no speaker or no mic)
     */
    AEC_SIDETONE_NOT_REQUIRED,

    /* Sidetone mixing is being done in HW */
    AEC_SIDETONE_IN_HW,

    /* Sidetone mixing is done in SW in traditional way,
     * MIC graph provides sidetone samples and SPKR graph
     * mixes them into speaker.
     */
    AEC_SIDETONE_IN_SW_BY_MIC_SPKR_GRAPH,
#ifdef AEC_REFERENCE_CAN_PERFORM_INPLACE_SIDETONE_MIXING
    /* Sidetone mixing is done in SW using dedicated
     * sidetone graph, this method is used instead of
     * AEC_SIDETONE_IN_SW_BY_MIC_SPKR_GRAPH when speaker
     * and mic graphs are running in long task periods.
     * Available only for Hydra architectures
     */
    AEC_SIDETONE_IN_SW_USING_SEPARATE_GRAPH
#endif
}AEC_REFERENCE_SIDETONE_METHOD;

/*
 * The latency between REFERENCE and MIC outputs are kept in acceptable range,
 * typically in range of 1ms to 2.5ms. Echo cancellers will be OK with any value
 * of latency in this range, however please notice that abrupt change in latencies
 * won't be acceptable and will cause echo cancellers to re converge. To meet this
 * requirement AEC_REFERENCE continuously synchronises REFERENCE output to MIC output,
 * this is done using applying the overall INPUT->OUTPUT to a rate adjustment in
 * REFERENCE path. However the absolute REF-OUT latency is still prone to long-run
 * drift, so there is a chance that after several hours it goes out of the acceptable
 * range. To avoid this we add a small fix to applied rate to REF path to make sure
 * the latency in overall moves toward the centre of the range. The fix is so extremely
 * small that won't cause any sudden change in latency
 */
/* max = 1e-7, means that for example if the latency is 0.5ms away from centre point,
 * it will take at least 5000 seconds to gradually shift it to centre
 */
#define AEC_REFERENCE_REF_MIC_ADJ_MAX  FRACTIONAL(1e-7)
/* determines the speed of convergence, but remember it is always
 * limited to AEC_REFERENCE_REF_MIC_MAX_ADJUST
 */
#define AEC_REFERENCE_REF_MIC_ADJ_COEFF 1

#define AEC_REF_SIDETONE_MIC_NONE  0xF      /* No mic is mixed into speaker */
#define AEC_REF_SIDETONE_MIC_SHIFT 4
#define AEC_REF_SIDETONE_MIC_MASK  0xF

typedef enum _validate_channels_error_codes {
    BUILD_GRAPHS_FAIL           = 0x1000,
    INCLUDE_REF_PATH_FAIL       = 0x2000,
    BUILD_SPKR_FAIL             = 0x0200,
    BUILD_MIC_FAIL              = 0x0300,
    INCLUDE_SIDETONE_FAIL       = 0x0400,
    BUILD_SIDETONE_FAIL         = 0x0500,
    MALLOC_FAIL                 = 0x0001,
    ALLOC_GRAPH_FAIL            = 0x0002,
    CREATE_AEC_SPKR_OP_FAIL     = 0x0003,
    CREATE_DC_REMOVE_OP_FAIL    = 0x0004,
    CREATE_MIXER_OP_FAIL        = 0x0005,
    CREATE_IIR_RESAMPLE_OP_FAIL = 0x0006,
    CREATE_RATE_ADJUST_OP_FAIL  = 0x0007,
    CREATE_SHIFT_OP_FAIL        = 0x0008,
    CREATE_RATE_MONITOR_OP_FAIL = 0x0009,
    CREATE_MUTE_OP_FAIL         = 0x000a,
    CREATE_MIC_LATENCY_OP_FAIL  = 0x000b
} validata_channels_error_codes_t;

/* Extended data for Capability */
typedef struct aec_ref_root {
    tCbuffer *input_stream[AEC_REF_NUM_SINK_TERMINALS];          /**< Pointer to Sink Terminals  */
    tCbuffer *output_stream[AEC_REF_NUM_SOURCE_TERMINALS];       /**< Pointer to Source Terminals  */

    tCbuffer *scratch_bufs[AEC_NUM_SCRATCH_BUFFERS];            /**< Pointer to buffers  */

    tCbuffer  *sidetone_buf;                                    /**< Pointer to sidetone buffer  */
    unsigned  *resampler_temp_buffer;
    unsigned resampler_temp_buffer_size;                        /* buffer size for temp buffer used in resampler */

    AEC_REFERENCE_PARAMETERS params;                            /**< Current parameters */

    SHARED_VOLUME_CONTROL_OBJ *shared_volume_ptr;               /**< Pointer to volume information */

    unsigned cap_id;            /**< Capability ID  */
    unsigned Cur_mode;          /**< Current operating mode  */
    unsigned Ovr_Control;       /**< OBPM override setting  */
    bool     ReInitFlag;        /**< Initialization flag  */

    tTimerId kick_id;           /**< Timer Task ID  */

    CPS_PARAM_DEF parms_def;

    unsigned task_period;       /* Operator's Task Period in microseconds,
                                 * set by user via operator message, possible values are:
                                 * 1000, 1250, 1600, 2000, 2500, 3125, 4000 and 5000
                                 * default value=1000us
                                 */

    unsigned task_frequency;    /* Operator's main task runs per second */
    unsigned task_period_frac;  /* Task period as a fraction of second */
    unsigned kick_period;       /* Kick period = task_period/decim_factor */
    unsigned kick_period_frac;  /* Kick period as a fraction of second (used for sidetone graph) */
    unsigned max_io_rate;       /* max rate among all mics/spkrs/inputs and outputs*/
#ifdef AEC_REFERENCE_CAN_PERFORM_INPLACE_SIDETONE_MIXING
    unsigned task_decim_factor; /* decimation factor for task period to
                                 * run sidetone mixing more frequently */
    unsigned task_decim_counter; /* counter holding decimation state */
    cbops_graph *sidetone_graph; /* standalone cbops graph for sidetone mixing */
#ifdef AEC_REFERENCE_ENABLE_LOW_LATENCY_PATH
    tCbuffer *sidetone_mic_buf_arr[AEC_REFERENCE_MAX_SIDETONE_MICS];  /* clone cbuffer structure for mic input to sidetone graph */
#endif
    tCbuffer *sidetone_mic_buf; /* clone cbuffer structure for mic input to sidetone graph */
#ifdef AEC_REFERENCE_SYNC_SIDETONE_GRAPH_TO_SPKR    
    RATEMATCHING_SUPPORT sidetone_graph_sync_type;
#endif
#endif
    bool hw_sidetone_available;  /* Whether hw sidetone mixing is available (not implemented yet)*/

    AEC_REFERENCE_SIDETONE_METHOD sidetone_method; /* current sidetone mixing method */

    unsigned input_rate;        /**< Input Sample Rate */
    unsigned output_rate;       /**< Output Sample Rate */

    unsigned    mic_rate;               /**< Sink Sample Rate */
    RATEMATCHING_SUPPORT mic_rate_ability;   /**< Sink rate matching ability from IO*/
    RATEMATCHING_SUPPORT mic_rate_enactment; /**< Sink rate matching - enabled*/
    unsigned    mic_rate_adjustment;    /**< Sink rate adjustment */
    cbops_graph *mic_graph;             /**< Pointer to Sink cbops graph */
    cbops_op    *mic_rate_monitor_op;   /**< Pointer to Sink cbops rate monitor  */
    cbops_op    *mic_sidetone_op;       /**< Pointer to cbops sidetone filter */
#ifdef INSTALL_AEC_REFERENCE_HOWL_LIMITER
    cbops_op    *mic_howling_limiter_op;/**< Pointer to cbops howling limiter */
#endif /* INSTALL_AEC_REFERENCE_HOWL_LIMITER */
    cbops_op    *mic_mute_op;           /* Pointer to cbops mute operator */
    bool        mic_mute_enable_config; /* user config for muting mic path */
    int         mic_shift;              /**< Input data shift to 24 bit */

    unsigned   spkr_rate;               /**< Source Sample Rate  */
    unsigned   spkr_rate_ability;       /**< Source rate matching ability */
    unsigned   spkr_rate_adjustment;    /**< Source rate adjustment */
    unsigned   spkr_rate_enactment;     /**< Source rate matching - enabled*/
    cbops_graph *spkr_graph;            /**< Pointer to Source cbops graph */
    cbops_op    *spkr_rate_monitor_op;  /**< Pointer to Source cbops rate monitor */
    int         spkr_shift;             /**< Output data shift to 24 bit */
    unsigned    spkr_kick_size;         /**< Input threshold for kicks */

    unsigned    channel_status;         /**< Bitwise flag of terminal configuration */
    unsigned    num_spkr_channels;      /**< Number of speaker channels */
    unsigned    num_mic_channels;       /**< Number of MIC channels */
    bool        spkr_sidetone_active;   /* flag showing speaker graph is mixing sidetone */
    bool        spkr_ref_active;        /* flag showing speaker graph is outputting reference signal */
    unsigned    spkr_ref_input_idx;     /* the buffer index used for reference path */
    unsigned    spkr_ref_scratch_idx;   /* the scratch buffer used in reference sub path */
    unsigned    spkr_stmix_in_idx;      /* buffer index where sidetone mix gets its input */
    unsigned    spkr_ref_idx;           /* buffer index reserved for reference output */
    unsigned    spkr_st_in_idx;         /* buffer index reserved for sidetone input */
    unsigned    spkr_num_ref_ops;       /* number of operators in reference sub-path */
    unsigned    spkr_ref_rs_idx;        /* buffer index used for resampler in reference path */
    cbops_op    *spkr_ref_point_op;     /* cbops operator to insert reference path after */
    cbops_op    *spkr_st_point_op;      /* cbops operator to insert sidetone part after */
    cbops_op    *spkr_ref_last_op;      /* last cbops operator in speaker graph's ref path */
    cbops_op    *spkr_stmix_op;         /* speaker sidetone mix operator */
    cbops_op    *spkr_ref_rs_op;        /* cbops op for resampler in reference path */
    unsigned    mic_st_idx;             /* buffer index used for sidetone output buffer */
    unsigned    mic_num_st_ops;         /* number of cbops operators in mic sidetone path */
    unsigned    mic_st_input_idx0;      /* first buffer index for the input of mic sidetone path */
    cbops_op    *mic_st_last_op;        /* last operator in mic sidetone path */
    cbops_op    *mic_st_point;          /* the operator in mic graph that we add sidetone path after */

    unsigned    output_period_size;     /* Output data per period */

    unsigned   using_sidetone;          /* Global for enabling sidetone feature */

    unsigned   source_kicks;            /* mask of sources to kick */
    unsigned   sink_kicks;              /* mask of sinks to kick */

    cbops_op    *mic_sw_rateadj_op;     /**< Pointer to cbops sw rate adjustment operator */
    cbops_op    *ref_sw_rateadj_op;     /* cbops rate adjust for reference path */
    cbops_op    *spkr_sw_rateadj_op;    /**< Pointer to cbops sw rate adjustment operator*/
#ifdef INSTALL_DELEGATE_RATE_ADJUST_SUPPORT
    EXT_OP_ID mic_ext_rate_adjust_op;  /**< external rate adjust operator for using by mic path */
    EXT_OP_ID spkr_ext_rate_adjust_op; /**< external rate adjust operator for using by spkr path */
#endif

    OVERRIDE_EP_HANDLE  mic_endpoint;      /**< Handle for overridden mic endpoint connected*/
    OVERRIDE_EP_HANDLE  spkr_endpoint;     /**< Handle for overridden speaker endpoint connected*/

    OVERRIDE_EP_HANDLE  mic_out_endpoint;      /**< Handle for overridden mic endpoint connected*/
    OVERRIDE_EP_HANDLE  spkr_in_endpoint;     /**< Handle for overridden speaker endpoint connected*/

    unsigned    spkr_rate_meas;
    unsigned    mic_rate_meas;
    unsigned spkr_in_threshold;              /* minimum expected amount in the speaker input buffer */
    unsigned spkr_out_threshold;             /* expected amount in the speaker output buffer */
    unsigned spkr_sink_delay;                /* extra latency that happens after samples leaving
                                              * speaker audio buffer, e.g. in an external coded */
    aec_latency_common sync_block;
#ifdef AEC_REFERENCE_SPKR_TTP
    bool spkr_timed_playback_mode;           /* flag showing whether speaker graph is in timed playback mode*/
    TIME spkr_last_timestamp;                /* timestamp associated with last read sample */
    bool spkr_last_timestamp_valid;          /* whether spkr_last_timestamp is valid */
    bool spkr_void_tag_observed;             /* flag shows last time looked at metadata a void tag seen at head */
    unsigned spkr_void_tag_counter;          /* counter showing the duration we have seen void tags */
    TIME_INTERVAL spkr_error_threshold;      /* current error threshold for TTP playback */
    ttp_pid_controller *spkr_pid;            /* TTP PID controller structure */
    TIME_INTERVAL spkr_last_ttp_error;       /* last ttp error */
    int spkr_ttp_error_acc;                  /* accumulator for ttp error averaging */
    unsigned spkr_ttp_error_acc_cnt;         /* counter for ttp error averaging */
    unsigned spkr_extra_delay;               /* total delay to add when computing ttp error, this will be
                                              * spkr_sink_delay plus extra internal delays like algorithmic
                                              * delay in cbops processing */
    unsigned spkr_ttp_adjust_type;           /* doing HW or SW rate adjust, separate for TTP for further flexibility */
#endif
    tCbuffer *spkr_input_metadata_buffer;    /* metadata buffer used for speaker graph input */
#ifdef AEC_REFERENCE_GENERATE_MIC_TIMESTAMP
    tCbuffer *mic_metadata_buffer;           /* metadata buffer for mic outputs */
    unsigned mic_metadata_tag_left_words;    /* words left to complete previously written tag */
    unsigned mic_metadata_min_tag_len;       /* minimum tag len for mic output metadata */
    bool mic_metadata_enable;                /* flag showing whether mic metadata is supported */
    unsigned mic_target_latency;             /* target latency for mic output timestamp tags */
    ttp_context *mic_time_to_play;           /* time to play context */
#endif /* AEC_REFERENCE_GENERATE_MIC_TIMESTAMP */

    unsigned ref_update_counter;             /* counter to decimate updating of the rate for reference path */
#ifdef AEC_REF_CALC_SPKR_RATE_MONITOR_AMOUNT
    unsigned spkr_last_address;              /* previous buffer address */
    unsigned spkr_new_amount;                /* new amount read by speaker */
    bool spkr_flow_started;                  /* speaker has started consuming */
#endif
    bool mic_sync_enable;                    /* flag showing backend input and output have
                                              * same clock so we want to sync mic to spkr input
                                              */
    unsigned output_buffer_size;             /* required buffer size for output terminals, if 0 default
                                              * value of 8.7ms will be used */
    unsigned input_buffer_size;              /* required buffer size for input terminals, if 0 default
                                              * value of 3ms will be used */
    unsigned spkr_buffer_size;               /* required buffer size for spkr terminals, if 0 the operator
                                              * wont impose minimum size for speaker buffers */
    unsigned mic_buffer_size;                /* required buffer size for mic terminals, if 0 the operator
                                              * wont impose minimum size for mic buffers */
#ifdef AEC_REFERENCE_ENABLE_LOW_LATENCY_PATH
    bool low_latency_st_path_enabled;        /* flag indicating if the low latency sidetone path is enabled */
    cbops_op *custom_op;                     /* cbop custom operator used in the low latency framework */
#endif /*  AEC_REFERENCE_ENABLE_LOW_LATENCY_PATH */
#if defined(IO_DEBUG)
    aec_latency_common *aec_latency_ptr;
    cbops_op *insert_op;
    cbops_op *st_disgard_op;
    unsigned ref_spkr_refdrop;
    unsigned ref_micref_delay;
    unsigned ref_st_drop;
    unsigned ref_inserts;
    unsigned ref_last_inserts_total;
    unsigned ref_last_inserts_insert;
    unsigned ref_last_inserts_wrap;
#endif

     /* small amount of fix applied to reference rate adjust to make sure
     * ref-mic output latency always tends to the centre of acceptable range.
     * No initialisation is required for this field since it is always limit
     * checked and limits are extremely low.
     */
    int ref_mic_adj_fix;

    /* flag shows mic graph has no output,
     * valid only when mic_graph != NULL
     */
    bool mic_graph_no_output;

    /* output block size configured by the user, if 0 the operator
     * will use the block size that inherits from connected operator.
     */
    unsigned cfg_output_block_size;

    /* number of mics that are used for sidetone, this currently can be 0 or 1 only */
    unsigned num_sidetone_mics;

    /* stores indexes for mics that are used for sidetone, first num_sidetone_mics are valid */
    unsigned sidetone_mics[AEC_REFERENCE_MAX_SIDETONE_MICS];

    unsigned ref_latency_index; /* buffer index of latency operator in cbops spkr_graph */
    unsigned mic_latency_index; /* buffer index of latency operator in cbops mic_graph */

    unsigned hw_sidetone_vars[6];             /* some variables for sidetone hw
                                               * (reserved to be used later. */
#ifdef ENABLE_STEREO_REFERENCE
    unsigned num_ref_channels;
    unsigned spkr_num_idxs;                 // Used in the reference sub-path
#endif
    unsigned validata_channels_error_code;    // An error code from the validate channels build
#ifdef FORCE_RESYNC_ENABLE
    unsigned mic_force_resync;
#endif

#if (defined INSTALL_TOD_STATS) && (!defined RUNNING_ON_KALSIM) 
    /*Store OP Data pointer to get the required endpoint info for ToD Stats. This way is chosen to have minimal change from the base */
    OPERATOR_DATA *op_data_aec_ref;
#endif
} AEC_REFERENCE_OP_DATA;

/****************************************************************************
Private Function Definitions
*/

/*  add connection statistic
        4 sample rates
        terminal connectivity word

        | A1 | A2 | SPKR channels (8-bits) | MIC channels (4-bits)

        A1:  0 = full SPKR Channels, 1=channel Mixing/Split
        A2:  0 = Mixing, 1 = Splitting

    */


/* Receive capability handler functions declarations */
extern bool aec_reference_create(OPERATOR_DATA *op_data, void *message_data, unsigned *response_id, void **response_data);
extern bool aec_reference_destroy(OPERATOR_DATA *op_data, void *message_data, unsigned *response_id, void **response_data);
extern bool aec_reference_stop_reset(OPERATOR_DATA *op_data,void *message_data, unsigned *response_id, void **response_data);
extern bool aec_reference_connect(OPERATOR_DATA *op_data, void *message_data, unsigned *response_id, void **response_data);
extern bool aec_reference_disconnect(OPERATOR_DATA *op_data, void *message_data, unsigned *response_id, void **response_data);
extern bool aec_reference_start(OPERATOR_DATA *op_data, void *message_data, unsigned *response_id, void **response_data);
extern bool aec_reference_get_data_format(OPERATOR_DATA *op_data, void *message_data, unsigned *response_id, void **response_data);
extern bool aec_reference_buffer_details(OPERATOR_DATA *op_data, void *message_data, unsigned *response_id, void **response_data);
extern bool aec_reference_get_sched_info(OPERATOR_DATA *op_data, void *message_data, unsigned *response_id, void **response_data);


/* Data processing function */
extern void aec_reference_timer_task(void *kick_object);

/* Operator message handlers */
extern bool aec_reference_opmsg_obpm_set_control(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_opmsg_obpm_get_params(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_opmsg_obpm_get_defaults(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_opmsg_obpm_set_params(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_opmsg_obpm_get_status(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);

extern bool aec_reference_opmsg_ep_get_config(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_opmsg_ep_configure(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_opmsg_ep_clock_id(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);

extern bool aec_reference_set_rates(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_set_input_output_rates(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);

extern bool aec_reference_opmsg_set_ucid(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_opmsg_get_ps_id(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_opmsg_mute_mic_output(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_opmsg_set_task_period(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_set_task_period(AEC_REFERENCE_OP_DATA *op_extra_data, unsigned task_period, unsigned decim_factor);


extern bool aec_reference_opmsg_enable_mic_sync(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_opmsg_set_buffer_size(OPERATOR_DATA *op_data, void *message_data,
                                         unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);

extern void aec_reference_update_sidetone_status(AEC_REFERENCE_OP_DATA *op_extra_data);
#ifdef AEC_REFERENCE_SPKR_TTP
extern void aec_reference_spkr_ttp_run(OPERATOR_DATA *op_data, unsigned *max_to_process);
extern void aec_reference_spkr_ttp_init(AEC_REFERENCE_OP_DATA  *op_extra_data);
extern void aec_reference_spkr_ttp_update_last_timestamp(AEC_REFERENCE_OP_DATA   *op_extra_data, unsigned amount_read);
extern void aec_reference_spkr_ttp_terminate(AEC_REFERENCE_OP_DATA  *op_extra_data);
#endif

#ifdef INSTALL_DELEGATE_RATE_ADJUST_SUPPORT
extern void aec_reference_spkr_check_external_rate_adjust_op(AEC_REFERENCE_OP_DATA *op_extra_data);
extern void aec_reference_mic_check_external_rate_adjust_op(AEC_REFERENCE_OP_DATA *op_extra_data);
#endif

#ifdef AEC_REFERENCE_GENERATE_MIC_TIMESTAMP
extern bool aec_reference_opmsg_set_ttp_latency(OPERATOR_DATA *op_data, void *message_data,
                                     unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_opmsg_set_ttp_params(OPERATOR_DATA *op_data, void *message_data,
                                    unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern bool aec_reference_opmsg_set_latency_limits(OPERATOR_DATA *op_data, void *message_data,
                                        unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern void aec_reference_mic_generate_metadata_with_ttp(AEC_REFERENCE_OP_DATA *op_extra_data, unsigned samples);
#endif /* AEC_REFERENCE_GENERATE_MIC_TIMESTAMP */

#ifdef AEC_REFERENCE_ENABLE_LOW_LATENCY_PATH
extern bool aec_reference_opmsg_enable_low_latency_path(OPERATOR_DATA *op_data, void *message_data,
                                                        unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
#endif /* AEC_REFERENCE_ENABLE_LOW_LATENCY_PATH */

extern void aec_reference_set_mic_gains(OPERATOR_DATA *op_data);
extern void aec_reference_cleanup(OPERATOR_DATA *op_data);
extern bool build_mic_graph(AEC_REFERENCE_OP_DATA *op_extra_data);
extern bool build_spkr_graph(AEC_REFERENCE_OP_DATA *op_extra_data);
extern bool validate_channels_and_build(OPERATOR_DATA *op_data);
extern bool aec_reference_update_mic_channel_status(AEC_REFERENCE_OP_DATA *op_extra_data);
extern bool aec_reference_update_spkr_channel_status(AEC_REFERENCE_OP_DATA *op_extra_data);
extern bool aec_reference_update_ref_channel_status(AEC_REFERENCE_OP_DATA *op_extra_data);
extern void aec_reference_update_sidetone_method(AEC_REFERENCE_OP_DATA *op_extra_data);

extern bool aec_reference_build_graphs(AEC_REFERENCE_OP_DATA* op_extra_data, bool build_spkr_graph, bool build_mic_graph);

extern bool aec_reference_mic_spkr_include_sidetone(AEC_REFERENCE_OP_DATA* op_extra_data, bool include_sidetone);
extern void aec_reference_cleanup_mic_graph(AEC_REFERENCE_OP_DATA *op_extra_data);
extern void aec_reference_cleanup_graphs(AEC_REFERENCE_OP_DATA *op_extra_data);
extern void aec_reference_cleanup_spkr_graph(AEC_REFERENCE_OP_DATA *op_extra_data);
extern bool aec_reference_spkr_include_ref_path(AEC_REFERENCE_OP_DATA* op_extra_data, bool include_ref_path);

extern bool aec_reference_stop_reset(OPERATOR_DATA *op_data,void *message_data, unsigned *response_id, void **response_data);

extern bool ups_params_aec(void* instance_data,PS_KEY_TYPE key,PERSISTENCE_RANK rank, uint16 length, unsigned* data, STATUS_KYMERA status,uint16 extra_status_info);
extern void aec_reference_update_mic_reference_sync(AEC_REFERENCE_OP_DATA * op_extra_data);
extern void aec_reference_init_hw_sidetone(AEC_REFERENCE_OP_DATA *op_extra_data);
extern void aec_reference_update_hw_sidetone(AEC_REFERENCE_OP_DATA *op_extra_data);
extern bool aec_reference_update_stream_and_validate_channels(OPERATOR_DATA *op_data, tCbuffer **bufp, tCbuffer *bufval);
extern bool aec_reference_opmsg_set_output_block_size(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data);
extern void aec_reference_set_output_block_size(AEC_REFERENCE_OP_DATA *op_extra_data, unsigned block_size);
extern void aec_reference_update_sidetone_mics(AEC_REFERENCE_OP_DATA *op_extra_data);
extern void aec_reference_mic_ref_latency_limit_control(AEC_REFERENCE_OP_DATA *op_extra_data);
extern bool aec_reference_allocate_scratch_buffers_for_cbops_graphs(AEC_REFERENCE_OP_DATA *op_extra_data);

inline static bool IsSpeakerTerminal(unsigned term_idx)
{
    return ( (term_idx==AEC_REF_SPKR_TERMINAL1)
          || (term_idx==AEC_REF_SPKR_TERMINAL2)
          || ( (term_idx>=AEC_REF_SPKR_TERMINAL3)
              && (term_idx<=AEC_REF_SPKR_TERMINAL8) ) );
}
inline static bool IsSpeakerInputTerminal(unsigned term_idx)
{
    return ( (term_idx==AEC_REF_INPUT_TERMINAL1)
          || (term_idx==AEC_REF_INPUT_TERMINAL2)
          || ( (term_idx>=AEC_REF_INPUT_TERMINAL3)
              && (term_idx<=AEC_REF_INPUT_TERMINAL8) ) );
}
inline static bool IsMicrophoneTerminal(unsigned term_idx)
{
    term_idx &= ~TERMINAL_SINK_MASK;

    if(term_idx < AEC_REF_MIC_TERMINAL1)
    {
        return FALSE;
    }
    if(term_idx <= AEC_REF_MIC_TERMINAL4)
    {
        return TRUE;
    }
    return (term_idx >= AEC_REF_MIC_TERMINAL5);

}

inline static bool IsMicrophoneOutputTerminal(unsigned term_idx)
{
    return ((AEC_REF_OUTPUT_TERMINAL1 <= term_idx &&
            AEC_REF_OUTPUT_TERMINAL4 >= term_idx) ||
            (AEC_REF_OUTPUT_TERMINAL5 <= term_idx &&
                         AEC_REF_OUTPUT_TERMINAL8 >= term_idx)
            );
}


inline static unsigned MicrophoneTerminalByIndex(unsigned i)
{
    if(i<4)
    {
        return (i + AEC_REF_MIC_TERMINAL1);
    }
    else
    {
        return ((i-4)+ AEC_REF_MIC_TERMINAL5);
    }
}

inline static unsigned OutputTerminalByIndex(unsigned i)
{
    if(i<4)
    {
        return (i + AEC_REF_OUTPUT_TERMINAL1);
    }
    else
    {
        return ((i-4) + AEC_REF_OUTPUT_TERMINAL5);
    }
}

inline static unsigned GetSidetoneMicForSpeaker(uint32 map, unsigned spkr_no)
{
   if(spkr_no > 7)
   {
       return  AEC_REF_SIDETONE_MIC_NONE;
   }

   return  (unsigned)((map >> (spkr_no * AEC_REF_SIDETONE_MIC_SHIFT)) & AEC_REF_SIDETONE_MIC_MASK);
}

/* macro to get a speaker terminal index by speaker number,
 * they aren't contiguous - see definitions of AEC_REF_SPKR_TERMINAL
 * numbering at the beginning of this file.
 */
#define SpeakerTerminalByIndex(idx)      ( (idx)<2 ?  AEC_REF_SPKR_TERMINAL1+(idx)  : (AEC_REF_SPKR_TERMINAL3-2)+(idx)  )

/* macro to get a speaker input terminal index by speaker number,
 * they aren't contiguous - see definitions of AEC_REF_INPUT_TERMINAL
 * numbering at the beginning of this file.
 */
#define SpeakerInputTerminalByIndex(idx) ( (idx)<2 ?  AEC_REF_INPUT_TERMINAL1+(idx) : (AEC_REF_INPUT_TERMINAL3-2)+(idx) )

/* Utility macros to get/set channel status for speaker, microphone and reference paths */
#define MIC_CHSTS_MASK  CHAN_STATE_MIC_MASK
#define SPKR_CHSTS_MASK (CHAN_STATE_SPKR_MASK                  |  \
                         AEC_REFERENCE_CONSTANT_CONN_TYPE_PARA |  \
                         AEC_REFERENCE_CONSTANT_CONN_TYPE_MIX)
#define REFERENCE_CHSTS_MASK AEC_REFERENCE_CONSTANT_CONN_TYPE_REF
#define GetMicChannelStatus(p)    ((p->channel_status) & MIC_CHSTS_MASK)
#define SetMicChannelStatus(p,x)  p->channel_status = (((p->channel_status) & (~MIC_CHSTS_MASK))|(x))
#define GetSpkrChannelStatus(p)   ((p->channel_status) & SPKR_CHSTS_MASK)
#define SetSpkrChannelStatus(p,x) p->channel_status = (((p->channel_status) & (~SPKR_CHSTS_MASK))|(x))
#define GetRefChannelStatus(p)   ((p->channel_status) & REFERENCE_CHSTS_MASK)
#define SetRefChannelStatus(p,x) p->channel_status = (((p->channel_status) & (~REFERENCE_CHSTS_MASK))|(x))

#endif  /* AEC_REFERENCE_CAP_C_H */


cbops_op* create_aec_ref_spkr_op(unsigned nr_inputs,unsigned* input_idx, unsigned *interm_idxs, unsigned in_threshold,unsigned nr_outputs,unsigned* output_idx,unsigned out_threshold, unsigned max_jitter);

#ifdef AEC_REFERENCE_CAN_PERFORM_INPLACE_SIDETONE_MIXING
extern cbops_op* create_aec_ref_sidetone_op(tCbuffer *mic_buf,
                                     unsigned mic_idx,
                                     tCbuffer **spkr_bufs,
                                     unsigned nr_spkrs,
                                     unsigned *sidetone_idx,
                                     unsigned spkr_threshold,
                                     unsigned safety_threshold);
extern void aec_ref_sidetone_initialise(cbops_op *);
extern bool build_sidetone_graph(AEC_REFERENCE_OP_DATA* op_extra_data);
#ifdef AEC_REFERENCE_ENABLE_LOW_LATENCY_PATH
extern bool build_sidetone_graph_with_low_latency_support(AEC_REFERENCE_OP_DATA *op_extra_data);
#endif
extern void aec_reference_cleanup_sidetone_graph(AEC_REFERENCE_OP_DATA *op_extra_data);
#endif
unsigned get_aec_ref_cbops_inserts_total(cbops_op *op);
unsigned get_aec_ref_cbops_insert_op_insert_total(cbops_op *op);
unsigned get_aec_ref_cbops_wrap_op_insert_total(cbops_op *op);
#ifdef AEC_REFERENCE_ENABLE_LOW_LATENCY_PATH
extern cbops_op* create_aec_ref_llm_custom_op(unsigned num_ip_mics,
                                              unsigned *input_idx,
                                              unsigned *output_idx);
extern void configure_aec_ref_llm_custom_op(cbops_op *op,
                                            cbops_aec_ref_llm_custom_op_ui *params);
#ifdef AEC_REFERENCE_SYNC_SIDETONE_GRAPH_TO_SPKR
extern void aec_reference_sidetone_graph_sync_to_spkr(AEC_REFERENCE_OP_DATA *op_extra_data);
#endif
#endif
